use axum::http::header::WWW_AUTHENTICATE;
use axum::http::{HeaderMap, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use serde_json::json;
use std::borrow::Cow;
use std::collections::HashMap;
use crate::middleware::response::MyResponse;

#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error("authentication required")]
    Unauthorized,
    /// Return `403 Forbidden`
    #[error("user may not perform that action")]
    Forbidden,
    /// Return `404 Not Found`
    #[error("request path not found")]
    NotFound,
    /// Return `422 Unprocessable Entity`
    #[error("error in the request body")]
    UnprocessableEntity {
        errors: HashMap<Cow<'static, str>, Vec<Cow<'static, str>>>,
    },
    #[error("an error occurred with the Uri")]
    InvalidUri(#[from] axum::http::uri::InvalidUri),

    #[error("an error occurred with the hyper")]
    Hyper(#[from] hyper::Error),

    #[error("an error occurred with the database")]
    Sqlx(#[from] sqlx::Error),

    #[error("an error occurred with the FromUtf8Error")]
    FromUtf8Error(#[from] std::string::FromUtf8Error),
    
    #[error("an error occurred with the serde_json::Error")]
    JsonError(#[from] serde_json::Error),

    #[error("an internal server error occurred")]
    Anyhow(#[from] anyhow::Error),
}

//错误处理 将对错误进行包装
//对错误进行匹配返回数据

impl IntoResponse for Error {
    fn into_response(self) -> Response {
        match self {
            Self::UnprocessableEntity { errors } => {
                #[derive(serde::Serialize)]
                struct Errors {
                    errors: HashMap<Cow<'static, str>, Vec<Cow<'static, str>>>,
                }
               return  (StatusCode::OK, MyResponse::UnprocessableEntity(json!( Errors { errors })).into()).into_response();
            }

            Self::Unauthorized => {
                (
                    StatusCode::OK,
                    [(WWW_AUTHENTICATE, HeaderValue::from_static("Token"))]
                        .into_iter()
                        .collect::<HeaderMap>(),
                    self.to_string(),
                ).into_response();
            }

            Self::InvalidUri(ref e)=>{
                (StatusCode::OK,MyResponse::UnprocessableEntity(json!(e.to_string())).into()).into_response();
            }
            Self::Hyper (ref e)=>{
                (StatusCode::OK,MyResponse::UnprocessableEntity(json!(e.to_string())).into()).into_response();
            }

            Self::Sqlx(ref e) => {
                (StatusCode::OK,MyResponse::UnprocessableEntity(json!(e.to_string())).into()).into_response();
            }

            Self::Anyhow(ref e) => {
                (StatusCode::OK,MyResponse::UnprocessableEntity(json!(e.to_string())).into()).into_response();
            }
            _ => (),
        }
        (StatusCode::OK, MyResponse::OtheError(json!("other_error")).into()).into_response()
    }
}